package lt.inkredibl.iit;

import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

public class Pixel
{
	private int _x;
	private int _y;
	
	private Pixel(int x, int y)
	{
		_x = x;
		_y = y;
	}
	
	public double amount(List<Point2D> p)
	{
		ContourIterable poly = polygonIntersectRectangle(new ContourIterable(p), _x, _y, _x+1, _y+1);
		return polygonArea(poly.getPoints());
	}
	
	public static Pixel inst(int x, int y)
	{
		return new Pixel(x, y);
	}
	
	public static ContourIterable polygonIntersectRectangle(Iterable<Line2D.Double> lines, double x1, double y1, double x2, double y2)
	{
		ContourIterable cit = constrain(lines, new ConstraintRight(x2));
		cit = constrain(cit, new ConstraintTop(y1));
		cit = constrain(cit, new ConstraintBottom(y2));
		cit = constrain(cit, new ConstraintLeft(x1));
		return cit;
	}
	
	private static ContourIterable constrain(final Iterable<Line2D.Double> p, Constraint c)
	{
		List<Line2D.Double> lines = new ArrayList<Line2D.Double>();
		
		for (Line2D.Double ln : p)
		{
			if (c.inside(ln))
			{
//				System.out.println("accepting line: "+ln.x1+", "+ln.y1+", "+ln.x2+", "+ln.y2);
				lines.add(ln);
			}
			else if (c.outside(ln))
			{
				// do not add lines behind the limit
//				System.out.println("dropping line: "+ln.x1+", "+ln.y1+", "+ln.x2+", "+ln.y2);
			}
			else // line crosses limit add part of it
			{
//				System.out.println("modifying line: "+ln.x1+", "+ln.y1+", "+ln.x2+", "+ln.y2);
				c.crop(ln);
//				System.out.println("\tmodified line: "+ln.x1+", "+ln.y1+", "+ln.x2+", "+ln.y2);
				lines.add(ln);
			}
		}

		List<Point2D> p2 = new ArrayList<Point2D>();

		for (Line2D.Double ln: lines)
		{
			double lastX = 0;
			double lastY = 0;
			if (p2.size() > 0)
			{
				Point2D lastPoint = p2.get(p2.size()-1);
				lastX = lastPoint.getX();
				lastY = lastPoint.getY();
			}
			if (p2.size() == 0 || lastX != ln.x1 || lastY != ln.y1)
			{
				p2.add(new Point2D.Double(ln.x1, ln.y1));
			}
			p2.add(new Point2D.Double(ln.x2, ln.y2));
		}
		
		return new ContourIterable(p2);
	}
	
	/**
	 * Calculates polygon area by summing simple triangulation. Calculation is exact only for convex polygons.
	 * @param p polygon
	 * @return area
	 */
	private static double polygonArea(List<Point2D> p)
	{
		if (p == null || p.size() < 3)
		{
			return 0;
		}
		double area = 0;
		int len = p.size() - 1;
		Point2D p0 = p.get(0);
		for (int i = 1; i < len; i++)
		{
			Point2D p_i = p.get(i);
			Point2D p_i1 = p.get(i+1);
			area += triangleArea(p0.distance(p_i), p_i.distance(p_i1), p_i1.distance(p0));
		}
		return area;
	}

	public static double triangleArea(double a, double b, double c)
	{
		double s = (a+b+c)/2;
		return Math.sqrt(s*(s-a)*(s-b)*(s-c));
	}

	public int getX()
	{
		return _x;
	}
	
	public int getY()
    {
	    return _y;
    }

	public void setLocation(int x, int y)
	{
		_x = x;
		_y = y;
	}

}
